#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "user_gpio.h"
#include "sdkconfig.h"

#if CONFIG_IDF_TARGET_ESP32
#define GPIO_OUTPUT_IO_0    23
#define GPIO_RELAY_IO_1    12
#define GPIO_RELAY_IO_2    13
#define GPIO_RELAY_IO_3    14
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_RELAY_IO_1) | \
		(1ULL << GPIO_RELAY_IO_2) | (1ULL << GPIO_RELAY_IO_3))

#define GPIO_INPUT_IO_0     0
#define GPIO_INPUT_IO_1     22
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))

#elif CONFIG_IDF_TARGET_ESP32S3
#define GPIO_OUTPUT_IO_0    1
#define GPIO_RELAY_IO_1    17
#define GPIO_RELAY_IO_2    18
#define GPIO_RELAY_IO_3    4
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_RELAY_IO_1) | \
		(1ULL << GPIO_RELAY_IO_2) | (1ULL << GPIO_RELAY_IO_3))

#define GPIO_INPUT_IO_0     0
#define GPIO_INPUT_IO_1     7
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))


#endif


#define ESP_INTR_FLAG_DEFAULT 0

QueueHandle_t gpio_evt_queue = NULL;

uint8_t status_led1 = 0;
uint8_t status_led2 = 0;
uint8_t status_led3 = 0;

uint8_t relay_status_flag[3] = {0};

void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

void input_gpio_task(void* arg)
{
    uint32_t io_num;
    uint8_t status_1 = 0;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
        //    printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));

            if (io_num == 0) {
                if (gpio_get_level(io_num) == 0) {
		    status_1 = status_led1;

                    status_1 = !status_1;
                    status_led3 = 0;
		    set_led1_status(status_1);
                }
            }
        }
    }
}

void set_led1_status(uint8_t status)
{
	status_led1 = status;
}

uint8_t get_led1_status(void)
{
	return status_led1;
}


void set_relay_status(uint8_t index, uint8_t status)
{
	relay_status_flag[index] = status;
}

uint8_t get_relay_status(uint8_t index)
{
	return relay_status_flag[index];
}

void user_gpio_init(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    //disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    //interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);
}

void user_gpio_task(void *arg)
{

    uint8_t led_status_1 = 0;
    uint8_t relay_status[3] = {0};
    user_gpio_init();
    //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    xTaskCreate(input_gpio_task, "input_gpio_task", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    //remove isr handler for gpio number.
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin again
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

    printf("Minimum free heap size: %"PRIu32" bytes\n", esp_get_minimum_free_heap_size());

    // int cnt = 0;
    while(1) {
        // printf("cnt: %d\n", cnt++);
	
	if (led_status_1 != status_led1) {
		led_status_1 = status_led1;    
		gpio_set_level(GPIO_OUTPUT_IO_0, status_led1);
	}

	if (relay_status[0] != relay_status_flag[0]) {
		relay_status[0] = relay_status_flag[0];
		gpio_set_level(GPIO_RELAY_IO_1, relay_status[0]);
	}
	if (relay_status[1] != relay_status_flag[1]) {
		relay_status[1] = relay_status_flag[1];
		gpio_set_level(GPIO_RELAY_IO_2, relay_status[1]);
	}
	if (relay_status[2] != relay_status_flag[2]) {
		relay_status[2] = relay_status_flag[2];
		gpio_set_level(GPIO_RELAY_IO_3, relay_status[2]);
	}
       	vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}
